﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using davejk.Minecraft.ServerWrapper.Game;

namespace davejk.Minecraft.ServerWrapper
{
    public partial class MainWindow : Form
    {
        private string TitleFormat;

        private MinecraftServer Server;

        private bool ShouldBeRunning; //Do we want the server to be running? (used for crash detection)
        private bool AutoRecover;

        public MainWindow()
        {
            InitializeComponent();
            TitleFormat = this.Text;

            StatusProgress.Visible = false;

            PlayerListSelectionChanged(new object { }, new EventArgs { });

            //TODO: Get this setting from a file
            AutoRecover = MServerAutoRecover.Checked;

            if (!Utils.SetupHelper.IsServerInstalled())
            {
                Utils.ServerSetup SetupWindow = new Utils.ServerSetup();
                SetupWindow.ShowDialog();
            }

            if (Utils.ConfigHelper.ConfigFileExists)
            {
                UpdateTitleText();
                if (Utils.ConfigHelper.GetNamedProperty("online-mode") != "true")
                {
                    MessageBox.Show("User verification is turned off. This will let players to spoof their username, " +
                    "allowing them to imitate ops and evade bans.\n\nWould you like to turn verification on?"
                    , "User verification", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                    //TODO: Implement fixing of this setting
                }
            }
            else
            {
                this.Text = String.Format(TitleFormat, "Unconfigured server");
            }

            Server = new MinecraftServer();
            //TODO: Load these values from config
            Server.AllowGive = MCommandsAllowGive.Checked;
            Server.AllowGoto = MCommandsAllowGoto.Checked;

            //Server events
            Server.Output += new MinecraftServer.OutputHandler(PrintOutput);
            Server.Chat += new MinecraftServer.ChatHandler(PrintChat);
            Server.StateChanged += new MinecraftServer.StateChangedHandler(ServerStateChanged);
            Server.MapGenProgress += new MinecraftServer.MapGenProgressHandler(ServerMapProgress);
            Server.PlayerJoined += new MinecraftServer.PlayerJoinedHandler(ServerPlayerJoined);
            Server.PlayerLeft += new MinecraftServer.PlayerLeftHandler(ServerPlayerLeft);

            ShouldBeRunning = Server.Running;

            UpdateControls(false);

            AppendRichText("Minecraft Server Wrapper v" + Application.ProductVersion + Environment.NewLine, Color.Black);
        }

        private void AppendRichText(string text, Color color)
        {
            try
            {
                int stpos = ServerOutput.Text.Length;
                ServerOutput.AppendText(text);
                int stlen = ServerOutput.Text.Length - stpos;
                ServerOutput.Select(stpos, stlen);
                ServerOutput.SelectionColor = color;

                ServerOutput.SelectionStart = this.ServerOutput.Text.Length - 1;
                ServerOutput.ScrollToCaret();
            }
            catch (ObjectDisposedException ex) { }
        }

        #region Server events

        private void ServerStateChanged(bool running)
        {
            UpdateControls(running);

            if (running)
                UpdateTitleText();

            if (!running && ShouldBeRunning && AutoRecover)
            {
                Server.Start();
            }
        }

        public delegate void ServerPlayerJoinedCallback(MinecraftPlayer Player, int PlayerCount);
        private void ServerPlayerJoined(MinecraftPlayer Player, int PlayerCount)
        {
            if (this.InvokeRequired)
            {
                ServerPlayerJoinedCallback d = new ServerPlayerJoinedCallback(ServerPlayerJoined);
                this.Invoke(d, new object[] { Player, PlayerCount });
            }
            else
            {
                AppendRichText(Player.Name + " joined the game (" + Player.Ip + ":" +
                    Player.Port.ToString() + ")" + (Player.Op ? " (Op)" : "") +
                    Environment.NewLine, Color.Blue);

                PlayerList.Items.Add(Player);
            }
        }

        public delegate void ServerPlayerLeftCallback(string Name, int PlayerCount, string Reason);
        private void ServerPlayerLeft(string Name, int PlayerCount, string Reason)
        {
            if (this.InvokeRequired)
            {
                ServerPlayerLeftCallback d = new ServerPlayerLeftCallback(ServerPlayerLeft);
                this.Invoke(d, new object[] { Name, PlayerCount, Reason });
            }
            else
            {
                AppendRichText(Name + " left the game (" + Reason + ")" +
                    Environment.NewLine, Color.Blue);

                //TODO: Use LINQ for this
                foreach (MinecraftPlayer p in PlayerList.Items)
                {
                    if (p.Name == Name)
                    {
                        PlayerList.Items.Remove(p);
                        break;
                    }
                }
            }
        }

        public delegate void ServerMapProgressCallback(int percentage);
        private void ServerMapProgress(int percentage)
        {
            if (this.InvokeRequired)
            {
                ServerMapProgressCallback d = new ServerMapProgressCallback(ServerMapProgress);
                this.Invoke(d, new object[] { percentage });
            }
            else
            {
                if (percentage != 95)
                {
                    StatusProgress.Visible = true;
                    StatusText.Text = "Preparing world...";
                    StatusProgress.Value = percentage;
                } else {
                    StatusProgress.Visible = false;
                    StatusText.Text = "Ready";
                }
            }
        }

        private delegate void UpdateTitleTextCallback();
        private void UpdateTitleText()
        {
            if (this.InvokeRequired)
            {
                UpdateTitleTextCallback d = new UpdateTitleTextCallback(UpdateTitleText);
                this.Invoke(d);
            }
            else
            {
                if (Utils.ConfigHelper.ConfigFileExists)
                {
                    string WorldName = Utils.ConfigHelper.GetNamedProperty("level-name");
                    string ServerIp = Utils.ConfigHelper.GetNamedProperty("server-ip");
                    if (String.IsNullOrEmpty(ServerIp))
                        ServerIp = "*";
                    string ServerPort = Utils.ConfigHelper.GetNamedProperty("server-port");

                    this.Text = String.Format(TitleFormat, String.Format("{0} ({1}:{2})",
                        WorldName, ServerIp, ServerPort));
                }
                else
                {
                    this.Text = String.Format(TitleFormat, "New Server");
                }
            }
        }

        private delegate void UpdateControlsCallback(bool running);
        private void UpdateControls(bool running)
        {
            if (this.InvokeRequired)
            {
                UpdateControlsCallback d = new UpdateControlsCallback(UpdateControls);
                this.Invoke(d, new object[] { running });
            }
            else
            {
                if(!running)
                    PlayerList.Items.Clear();

                MServerStartStop.Text = (running ? "Stop" : "Start");
                MServerForceStop.Enabled = running;
                MToolsInstallMinecraft.Enabled = !running;
                MToolsPatch.Enabled = !running;
                MWorldAutosave.Checked = true;
                MWorldAutosave.Enabled = running;
                MWorldSave.Enabled = running;
                MHelpConsoleCmds.Enabled = running;
            }
        }

        private delegate void PrintOutputCallback(string output);
        private void PrintOutput(string output)
        {
            try
            {
                if (this.ServerOutput.InvokeRequired)
                {
                    PrintOutputCallback d = new PrintOutputCallback(PrintOutput);
                    this.Invoke(d, new object[] { output });
                }
                else
                {
                    if (output.StartsWith("[WARNING]"))
                        AppendRichText(output + Environment.NewLine, Color.Red);
                    else
                        AppendRichText(output + Environment.NewLine, Color.Black);
                }
            }
            catch (ObjectDisposedException ex) { }
        }

        private delegate void PrintChatCallback(string Name, string Message);
        private void PrintChat(string Name, string Message)
        {
            if (this.ServerOutput.InvokeRequired)
            {
                PrintChatCallback d = new PrintChatCallback(PrintChat);
                this.Invoke(d, new object[] { Name, Message });
            }
            else
            {
                AppendRichText("<", Color.Black);
                AppendRichText(Name, Color.Blue);
                AppendRichText(">", Color.Black);
                AppendRichText(" " + Message + Environment.NewLine, Color.Green);
            }
        }

        #endregion

        #region Menu

        #region Status bar help text

        private void MenuMouseEnter(object sender, EventArgs e)
        {
            try
            {
                StatusText.Text = ((ToolStripMenuItem)sender).Tag.ToString();
            }
            catch { }
        }
        private void MenuMouseEnterPlayer(object sender, EventArgs e)
        {
            try
            {
                StatusText.Text = String.Format(((ToolStripMenuItem)sender).Tag.ToString(), PlayerList.SelectedItem.ToString());
            }
            catch { }
        }
        private void MenuMouseEnterStartStop(object sender, EventArgs e)
        {
            try
            {
                StatusText.Text = String.Format(((ToolStripMenuItem)sender).Tag.ToString(),
                    (Server.Running ? "Stop" : "Start"));
            }
            catch { }
        }

        private void ButtonMouseEnter(object sender, EventArgs e)
        {
            try
            {
                StatusText.Text = ((Button)sender).Tag.ToString();
            }
            catch { }
        }

        private void ControlMouseLeave(object sender, EventArgs e)
        {
            //TODO: Reset the status text to the server's player count instead
            StatusText.Text = "Ready";
        }

        #endregion

        #region Server

        private void StartStopServer(object sender, EventArgs e)
        {
            if (Server.Running)
            {
                ShouldBeRunning = false;
                Server.Stop();
            }
            else
            {
                ShouldBeRunning = true;
                Server.Start();
            }
        }

        private void ToggleAutoRecover(object sender, EventArgs e)
        {
            AutoRecover = MServerAutoRecover.Checked;
        }

        private void ForceStopServer(object sender, EventArgs e)
        {
            Server.ForceStop();
            ControlMouseLeave(sender, e);
        }

        private void OpenServerSettings(object sender, EventArgs e)
        {
            ServerSettingsWindow SettingsWindow = new ServerSettingsWindow();
            SettingsWindow.ShowDialog();

            if (Server.Running)
            {
                if (MessageBox.Show("Any changes made to the server configuration will not take effect until it is restarted.\n\n" +
                    "Would you like to restart the server now?", "Restart Server?", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
                    == DialogResult.Yes)
                {
                    Server.Restart();
                }
            }
            else
            {
                UpdateTitleText();
            }
        }

        private void Quit(object sender, EventArgs e)
        {
            if (Server.Running)
            {
                if (MessageBox.Show("The server is currently running. Quitting the wrapper will also shut down the server."
                    + "\n\nDo you want to shut down the server and exit?", "Shut down server", MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    ShouldBeRunning = false;
                    Server.WaitForStop(30000);
                    Application.Exit();
                }
            }
            else
            {
                Application.Exit();
            }
        }

        #endregion

        #region World

        private void AutosaveToggled(object sender, EventArgs e)
        {
            Server.SendInput((MWorldAutosave.Checked ? "save-on" : "save-off"));
        }

        private void ForceSave(object sender, EventArgs e)
        {
            Server.SendInput("save-all");
        }

        private void SaveAs(object sender, EventArgs e)
        {
            Utils.SaveWorldAs SaveDialog = new Utils.SaveWorldAs(Utils.ConfigHelper.GetNamedProperty("level-name"));
            if (SaveDialog.ShowDialog() == System.Windows.Forms.DialogResult.Yes)
            {
                string SourcePath = Utils.SetupHelper.AppBaseDirectory + "\\Minecraft\\" + Utils.ConfigHelper.GetNamedProperty("level-name");
                string SavePath = Utils.SetupHelper.AppBaseDirectory + "\\Minecraft\\" + SaveDialog.SaveChoice;

                System.Diagnostics.Process CopyProc = new System.Diagnostics.Process();
                CopyProc.StartInfo.UseShellExecute = true;
                CopyProc.StartInfo.FileName = @"C:\WINDOWS\system32\xcopy.exe";
                CopyProc.StartInfo.Arguments = "\"" + SourcePath + "\" \"" + SavePath + "\" /E /I";
                CopyProc.Start();
            }
        }

        #endregion

        #region Backup
        #endregion

        #region Player

        private void KickSelectedPlayer(object sender, EventArgs e)
        {
            Server.SendInput("kick " + PlayerList.SelectedItem.ToString());
            try
            {
                PlayerList.Items.Remove(PlayerList.SelectedItem);
                ControlMouseLeave(sender, e);
            }
            catch { }
        }

        private void BanSelectedPlayer(object sender, EventArgs e)
        {
            Server.SendInput("ban " + PlayerList.SelectedItem.ToString());
            try
            {
                PlayerList.Items.Remove(PlayerList.SelectedItem);
                ControlMouseLeave(sender, e);
            }
            catch { }
        }

        private void ToggleOp(object sender, EventArgs e)
        {
            Server.SendInput((MPlayerOp.Checked ? "op " : "deop ") + PlayerList.SelectedItem.ToString());
        }

        #endregion

        #region Commands

        private void AllowGiveToggled(object sender, EventArgs e)
        {
            Server.AllowGive = MCommandsAllowGive.Checked;
        }

        private void AllowGotoToggled(object sender, EventArgs e)
        {
            Server.AllowGoto = MCommandsAllowGoto.Checked;
        }

        #endregion

        #region Tools

        private void RedownloadServer(object sender, EventArgs e)
        {
            //TODO: Disable this menu item when the server is running
            if (!Server.Running)
            {
                Utils.ServerSetup ServerSetup = new Utils.ServerSetup();
                ServerSetup.ShowDialog();
            }
        }

        #endregion

        #region Help

        private void OpenDocumentation(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://minecraft.codeplex.com/documentation");
        }

        private void ShowServerHelp(object sender, EventArgs e)
        {
            Server.SendInput("help");
        }

        private void OpenMinecraftSite(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://www.minecraft.net/");
        }

        private void OpenNotchSite(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://notch.tumblr.com/");
        }

        #endregion

        #endregion

        #region Input box

        private void SendInputAsChat(object sender, EventArgs e)
        {
            if (Server.Running)
            {
                Server.SendInput("say " + ServerInput.Text);
                ServerInput.Text = String.Empty;
            }
        }

        private void SendInputAsCmd(object sender, EventArgs e)
        {
            if (Server.Running)
            {
                Server.SendInput(ServerInput.Text);
                ServerInput.Text = String.Empty;
            }
        }

        private void InputKeyPress(object sender, KeyPressEventArgs e)
        {
            //TODO: Support modifier for sending as chat
            if (e.KeyChar == (char)13) {
                Server.SendInput(ServerInput.Text);
                ServerInput.Text = String.Empty;
            }
        }

        #endregion

        private void PlayerListSelectionChanged(object sender, EventArgs e)
        {
            if (PlayerList.SelectedIndex == -1)
            {
                MPlayerKick.Enabled = false;
                MPlayerKick.Text = "Kick";
                MPlayerBan.Enabled = false;
                MPlayerBan.Text = "Ban";
                MPlayerOp.Enabled = false;
                MPlayerOp.Checked = false;
                MCommandsGive.Enabled = false;
                MCommandsGive.Text = "Give item...";
            }
            else
            {
                MPlayerKick.Enabled = true;
                MPlayerKick.Text = String.Format("Kick {0}", PlayerList.SelectedItem.ToString());
                MPlayerBan.Enabled = true;
                MPlayerBan.Text = String.Format("Ban {0}", PlayerList.SelectedItem.ToString());
                MPlayerOp.Enabled = true;
                MPlayerOp.Checked = ((MinecraftPlayer)PlayerList.SelectedItem).Op;
                MCommandsGive.Enabled = true;
                MCommandsGive.Text = String.Format("Give {0} item...", PlayerList.SelectedItem.ToString());
            }
        }

    }
}
